﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Threading;
using System.Diagnostics;

namespace ParallelRuntimeLibrary.Collections
{
    class BlockingCollection<T>
    {
        private int mBoundedCapacity;
        private IConcurrentCollection<T> mCollection;
        private SemaphoreSlim mFreeNodes;
        private volatile int mIsAddingCompleted;
        private volatile bool mIsDisposed;
        private SemaphoreSlim mOccupiedNodes;

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> class without an upper-bound.
        /// </summary>
        /// <remarks>
        /// The default underlying collection is a <see cref="T:System.Threading.Collections.ConcurrentQueue`1">ConcurrentQueue&lt;T&gt;</see>.
        /// </remarks>
        public BlockingCollection()
            : this(new ConcurrentQueue<T>())
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />
        /// class with the specified upper-bound.
        /// </summary>
        /// <param name="boundedCapacity">The bounded size of the collection.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="boundedCapacity" /> is
        /// not a positive value.</exception>
        /// <remarks>
        /// The default underlying collection is a <see cref="T:System.Threading.Collections.ConcurrentQueue`1">ConcurrentQueue&lt;T&gt;</see>.
        /// </remarks>
        public BlockingCollection(int boundedCapacity)
            : this(new ConcurrentQueue<T>(), boundedCapacity)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />
        /// class without an upper-bound and using the provided 
        /// <see cref="T:System.Threading.Collections.IConcurrentCollection{T}" /> as its underlying data store.</summary>
        /// <param name="collection">The collection to use as the underlying data store.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection" /> argument is
        /// null.</exception>
        public BlockingCollection(IConcurrentCollection<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");
            
            this.Initialize(collection, -1);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />
        /// class with the specified upper-bound and using the provided 
        /// <see cref="T:System.Threading.Collections.IConcurrentCollection{T}" /> as its underlying data store.</summary>
        /// <param name="collection">The collection to use as the underlying data store.</param>
        /// <param name="boundedCapacity">The bounded size of the collection.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collection" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException">The <paramref name="boundedCapacity" /> is not a positive value.</exception>
        /// <exception cref="T:System.ArgumentException">The supplied <paramref name="collection" /> contains more values 
        /// than is permitted by <paramref name="boundedCapacity" />.</exception>
        public BlockingCollection(IConcurrentCollection<T> collection, int boundedCapacity)
        {
            if (boundedCapacity < 1)
                throw new ArgumentOutOfRangeException("boundedCapacity", "The boundedCapacity value must be positive");
            
            if (collection == null)
                throw new ArgumentNullException("collection");
            
            if (collection.Count > boundedCapacity)
                throw new ArgumentException("collection", "The collection count must be less than boundedCapacity");
            
            this.Initialize(collection, boundedCapacity);
        }


        /// <summary>
        /// Initializes the BlockingCollection instance.</summary>
        /// <param name="collection">The collection to use as the underlying data store.</param>
        /// <param name="boundedCapacity">The bounded size of the collection.</param>
        private void Initialize(IConcurrentCollection<T> collection, int boundedCapacity)
        {
            mCollection = collection;
            mBoundedCapacity = boundedCapacity;
            mIsAddingCompleted = 0;
            mIsDisposed = false;
            if (boundedCapacity == -1)
                mFreeNodes = null;
            else
                mFreeNodes = new SemaphoreSlim(boundedCapacity - this.mCollection.Count);
            
            mOccupiedNodes = new SemaphoreSlim(this.mCollection.Count);
        }

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Properties

        /// <summary>
        /// Gets the bounded capacity of this <see cref="T:ParallelRuntimeLibrary.Collections.BlockingCollection{T}" /> instance.</summary>
        /// <value>The bounded capacity of this collection, or int.MaxValue if no bound was supplied.</value>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:ParallelRuntimeLibrary.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        public int BoundedCapacity
        {
            get
            {
                this.CheckDisposed();
                return this.mBoundedCapacity;
            }
        }

        /// <summary>
        /// Gets the number of items contained in the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.</summary>
        /// <value>The number of items contained in the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.</value>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        public int Count
        {
            get
            {
                this.CheckDisposed();
                return this.mOccupiedNodes.CurrentCount;
            }
        }

        /// <summary>
        /// Gets whether this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked as complete for adding.</summary>
        /// <value>Whether this collection has been marked as complete for adding.</value>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        public bool IsAddingCompleted
        {
            get
            {
                this.CheckDisposed();
                return (this.mIsAddingCompleted == 1);
            }
        }

        /// <summary>
        /// Gets whether this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked as complete for adding and is empty.</summary>
        /// <value>Whether this collection has been marked as complete for adding and is empty.</value>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        public bool IsCompleted
        {
            get
            {
                this.CheckDisposed();
                return ((this.mIsAddingCompleted == 1) && (this.mOccupiedNodes.CurrentCount == 0));
            }
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Basic Operation

        /// <summary>
        /// Adds the item to the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item to be added to the collection. The value can be a null reference.</param>
        /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <remarks>
        /// If a bounded capacity was specified when this instance of 
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> was initialized, 
        /// a call to <see cref="M:System.Threading.Collections.BlockingCollection`1.Add(`0)" /> may block until space is available to store the provided item.
        /// </remarks>
        public void Add(T item)
        {
            this.TryAdd(item, -1);
        }

        /// <summary>Removes an item from the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.</summary>
        /// <returns>The item removed from the collection.</returns>
        /// <exception cref="T:System.OperationCanceledException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> is empty and has been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
        /// outside of this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        /// <remarks>A call to <see cref="M:System.Threading.Collections.BlockingCollection`1.Remove" /> may block until an item is available to be removed.</remarks>
        public T Remove()
        {
            T local;
            if (!this.TryRemove(out local, -1))
                throw new OperationCanceledException("");
            
            return local;
        }


        /// <summary>
        /// Attempts to add the specified item to the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item to be added to the collection.</param>
        /// <returns>true if the <paramref name="item" /> could be added; otherwise, false.</returns>
        /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        public bool TryAdd(T item)
        {
            return this.TryAdd(item, 0);
        }

        /// <summary>
        /// Attempts to add the specified item to the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item to be added to the collection.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite" /> (-1) to wait indefinitely.</param>
        /// <returns>true if the <paramref name="item" /> could be added to the collection within 
        /// the alloted time; otherwise, false.</returns>
        /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout" /> is a
        /// negative number other than -1, which represents an infinite time-out.</exception>
        public bool TryAdd(T item, int millisecondsTimeout)
        {
            BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
            return this.TryAddWithNoTimeValidation(item, millisecondsTimeout);
        }

        /// <summary>
        /// Attempts to add the specified item to the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item to be added to the collection.</param>
        /// <param name="timeout">A <see cref="T:System.TimeSpan" /> that represents the number of milliseconds
        /// to wait, or a <see cref="T:System.TimeSpan" /> that represents -1 milliseconds to wait indefinitely.
        /// </param>
        /// <returns>true if the <paramref name="item" /> could be added to the collection within 
        /// the alloted time; otherwise, false.</returns>
        /// <exception cref="T:System.InvalidOperationException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout" /> is a negative number
        /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than
        /// <see cref="F:System.Int32.MaxValue" />.</exception>
        public bool TryAdd(T item, TimeSpan timeout)
        {
            BlockingCollection<T>.ValidateTimeout(timeout);
            return this.TryAddWithNoTimeValidation(item, (int)timeout.TotalMilliseconds);
        }


        /// <summary>
        /// Attempts to remove an item from the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item removed from the collection.</param>
        /// <returns>true if an item could be removed; otherwise, false.</returns>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
        /// outside of this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        public bool TryRemove(out T item)
        {
            return this.TryRemove(out item, 0);
        }

        /// <summary>
        /// Attempts to remove an item from the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item removed from the collection.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite" /> (-1) to wait indefinitely.</param>
        /// <returns>true if an item could be removed from the collection within 
        /// the alloted time; otherwise, false.</returns>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout" /> is a
        /// negative number other than -1, which represents an infinite time-out.</exception>
        /// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
        /// outside of this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        public bool TryRemove(out T item, int millisecondsTimeout)
        {
            BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
            return this.TryRemoveWithNoTimeValidation(out item, millisecondsTimeout);
        }

        /// <summary>
        /// Attempts to remove an item from the <see cref="T:System.Threading.Collections.BlockingCollection{T}" />.
        /// </summary>
        /// <param name="item">The item removed from the collection.</param>
        /// <param name="timeout">A <see cref="T:System.TimeSpan" /> that represents the number of milliseconds
        /// to wait, or a <see cref="T:System.TimeSpan" /> that represents -1 milliseconds to wait indefinitely.
        /// </param>
        /// <returns>true if an item could be removed from the collection within 
        /// the alloted time; otherwise, false.</returns>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout" /> is a negative number
        /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than
        /// <see cref="F:System.Int32.MaxValue" />.</exception>
        /// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
        /// outside of this <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        public bool TryRemove(out T item, TimeSpan timeout)
        {
            BlockingCollection<T>.ValidateTimeout(timeout);
            return this.TryRemoveWithNoTimeValidation(out item, (int)timeout.TotalMilliseconds);
        }


        /// <summary>Adds an item into the underlying data store using its IConcurrentCollection&lt;T&gt;.Add 
        /// method. If a bounded capacity was specified and the collection was full, 
        /// this method will wait for, at most, the timeout period trying to add the item. 
        /// If the timeout period was exhaused before successfully adding the item this method will 
        /// return false.</summary>
        /// <param name="item">The item to be added to the collection.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait for the collection to accept the item,
        /// or Timeout.Infinite to wait indefinitely.</param>
        /// <returns>False if the collection remained full till the timeout period was exhausted.True otherwise.</returns>
        /// <exception cref="T:System.InvalidOperationException">the collection has already been marked
        /// as complete with regards to additions.</exception>
        /// <exception cref="T:System.ObjectDisposedException">If the collection has been disposed.</exception>
        private bool TryAddWithNoTimeValidation(T item, int millisecondsTimeout)
        {
            this.CheckDisposed();
            this.CheckAddingIsCompleted();

            bool flag = true;
            if (this.mFreeNodes != null)
            {
                try
                {
                    flag = this.mFreeNodes.Wait(millisecondsTimeout);
                }
                catch (OperationCanceledException)
                {
                    throw new InvalidOperationException();
                }
            }
            if (flag)
            {
                bool flag2 = false;
                try
                {
                    flag2 = this.mCollection.Add(item);
                }
                catch
                {
                    if (this.mFreeNodes != null)
                        BlockingCollection<T>.ReleaseSemaphore(this.mFreeNodes);
                    throw;
                }
                if (flag2)
                    BlockingCollection<T>.ReleaseSemaphore(this.mOccupiedNodes);
                
                this.CheckAddingIsCompleted();
            }
            return flag;
        }

        /// <summary>Removes an item from the underlying data store using its IConcurrentCollection&lt;T&gt;.Remove 
        /// method. If the collection was empty, this method will wait for, at most, the timeout period (if AddingIsCompleted is false)
        /// trying to remove an item. If the timeout period was exhaused before successfully removing an item 
        /// this method will return false.</summary>
        /// <param name="item">The item removed from the collection.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait for the collection to have an item available 
        /// for removal, or Timeout.Infinite to wait indefinitely.</param>
        /// <returns>False if the collection remained empty till the timeout period was exhausted. True otherwise.</returns>
        /// <exception cref="T:System.ObjectDisposedException">If the collection has been disposed.</exception>
        private bool TryRemoveWithNoTimeValidation(out T item, int millisecondsTimeout)
        {
            this.CheckDisposed();

            item = default(T);
            if (this.IsCompleted)
                return false;
            
            bool flag = false;
            try
            {
                flag = this.mOccupiedNodes.Wait(millisecondsTimeout);
            }
            catch (OperationCanceledException)
            {
                return false;
            }
            
            if (flag)
            {
                bool flag2 = false;
                try
                {
                    flag2 = this.mCollection.Remove(out item);
                }
                catch
                {
                    BlockingCollection<T>.ReleaseSemaphore(this.mOccupiedNodes);
                    throw;
                }

                if (!flag2)
                    throw new InvalidOperationException();
                
                if (this.mFreeNodes != null)
                    BlockingCollection<T>.ReleaseSemaphore(this.mFreeNodes);
                
                if (this.IsCompleted)
                    this.CancelSemaphores();
            }
            return flag;
        }

        #endregion

        #region Basic Static Operation

        /// <summary>
        /// Adds the specified item to any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item to be added to one of the collections.</param>
        /// <returns>The index of the collection in the <paramref name="collections" /> array to which the item was added.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element, or at least one of collections has been
        /// marked as complete for adding.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <remarks>
        /// If a bounded capacity was specified when all of the
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances were initialized, 
        /// a call to <see cref="M:System.Threading.Collections.BlockingCollection`1.AddAny(System.Threading.Collections.BlockingCollection{`0}[],`0)" /> may block until space is available in one of the collections
        /// to store the provided item.
        /// </remarks>
        public static int AddAny(BlockingCollection<T>[] collections, T item)
        {
            return BlockingCollection<T>.TryAddAny(collections, item, -1);
        }

        /// <summary>
        /// Attempts to add the specified item to any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item to be added to one of the collections.</param>
        /// <returns>The index of the collection in the <paramref name="collections" /> 
        /// array to which the item was added, or -1 if the item could not be added.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element, or at least one of collections has been
        /// marked as complete for adding.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        public static int TryAddAny(BlockingCollection<T>[] collections, T item)
        {
            return BlockingCollection<T>.TryAddAny(collections, item, 0);
        }

        /// <summary>
        /// Attempts to add the specified item to any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item to be added to one of the collections.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite" /> (-1) to wait indefinitely.</param>        /// <returns>The index of the collection in the <paramref name="collections" /> 
        /// array to which the item was added, or -1 if the item could not be added.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element, or at least one of collections has been
        /// marked as complete for adding.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout" /> is a
        /// negative number other than -1, which represents an infinite time-out.</exception>
        public static int TryAddAny(BlockingCollection<T>[] collections, T item, int millisecondsTimeout)
        {
            BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
            return BlockingCollection<T>.TryAddRemoveAny(collections, ref item, millisecondsTimeout, true);
        }

        /// <summary>
        /// Attempts to add the specified item to any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item to be added to one of the collections.</param>
        /// <param name="timeout">A <see cref="T:System.TimeSpan" /> that represents the number of milliseconds
        /// to wait, or a <see cref="T:System.TimeSpan" /> that represents -1 milliseconds to wait indefinitely.
        /// </param>
        /// <returns>The index of the collection in the <paramref name="collections" /> 
        /// array to which the item was added, or -1 if the item could not be added.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element, or at least one of collections has been
        /// marked as complete for adding.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout" /> is a negative number
        /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than
        /// <see cref="F:System.Int32.MaxValue" />.</exception>
        public static int TryAddAny(BlockingCollection<T>[] collections, T item, TimeSpan timeout)
        {
            BlockingCollection<T>.ValidateTimeout(timeout);
            return BlockingCollection<T>.TryAddRemoveAny(collections, ref item, (int)timeout.TotalMilliseconds, true);
        }


        /// <summary>
        /// Removes an item from any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item removed from one of the collections.</param>
        /// <returns>The index of the collection in the <paramref name="collections" /> array from which 
        /// the item was removed, or -1 if an item could not be removed.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.InvalidOperationException">At least one of the underlying collections was modified
        /// outside of its <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        /// <remarks>A call to <see cref="M:System.Threading.Collections.BlockingCollection`1.RemoveAny(System.Threading.Collections.BlockingCollection{`0}[],`0@)" /> may block until an item is available to be removed.</remarks>
        public static int RemoveAny(BlockingCollection<T>[] collections, out T item)
        {
            return BlockingCollection<T>.TryRemoveAny(collections, out item, -1);
        }

        /// <summary>
        /// Attempts to remove an item from any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item removed from one of the collections.</param>
        /// <returns>The index of the collection in the <paramref name="collections" /> array from which 
        /// the item was removed, or -1 if an item could not be removed.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.InvalidOperationException">At least one of the underlying collections was modified
        /// outside of its <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        /// <remarks>A call to <see cref="M:System.Threading.Collections.BlockingCollection`1.RemoveAny(System.Threading.Collections.BlockingCollection{`0}[],`0@)" /> may block until an item is available to be removed.</remarks>
        public static int TryRemoveAny(BlockingCollection<T>[] collections, out T item)
        {
            return BlockingCollection<T>.TryRemoveAny(collections, out item, 0);
        }

        /// <summary>
        /// Attempts to remove an item from any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item removed from one of the collections.</param>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite" /> (-1) to wait indefinitely.</param>
        /// <returns>The index of the collection in the <paramref name="collections" /> array from which 
        /// the item was removed, or -1 if an item could not be removed.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout" /> is a
        /// negative number other than -1, which represents an infinite time-out.</exception>
        /// <exception cref="T:System.InvalidOperationException">At least one of the underlying collections was modified
        /// outside of its <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        /// <remarks>A call to <see cref="M:System.Threading.Collections.BlockingCollection`1.RemoveAny(System.Threading.Collections.BlockingCollection{`0}[],`0@)" /> may block until an item is available to be removed.</remarks>
        public static int TryRemoveAny(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout)
        {
            BlockingCollection<T>.ValidateMillisecondsTimeout(millisecondsTimeout);
            T local = default(T);
            int num = BlockingCollection<T>.TryAddRemoveAny(collections, ref local, millisecondsTimeout, false);
            item = local;
            return num;
        }

        /// <summary>
        /// Attempts to remove an item from any one of the specified
        /// <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances.
        /// </summary>
        /// <param name="collections">The array of collections.</param>
        /// <param name="item">The item removed from one of the collections.</param>
        /// <param name="timeout">A <see cref="T:System.TimeSpan" /> that represents the number of milliseconds
        /// to wait, or a <see cref="T:System.TimeSpan" /> that represents -1 milliseconds to wait indefinitely.
        /// </param>
        /// <returns>The index of the collection in the <paramref name="collections" /> array from which 
        /// the item was removed, or -1 if an item could not be removed.</returns>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="collections" /> argument is
        /// null.</exception>
        /// <exception cref="T:System.ArgumentException">The <paramref name="collections" /> argument is
        /// a 0-length array or contains a null element.</exception>
        /// <exception cref="T:System.ObjectDisposedException">At least one of the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances has been disposed.</exception>
        /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout" /> is a negative number
        /// other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater than
        /// <see cref="F:System.Int32.MaxValue" />.</exception>
        /// <exception cref="T:System.InvalidOperationException">At least one of the underlying collections was modified
        /// outside of its <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance.</exception>
        /// <remarks>A call to <see cref="M:System.Threading.Collections.BlockingCollection`1.RemoveAny(System.Threading.Collections.BlockingCollection{`0}[],`0@)" /> may block until an item is available to be removed.</remarks>
        public static int TryRemoveAny(BlockingCollection<T>[] collections, out T item, TimeSpan timeout)
        {
            BlockingCollection<T>.ValidateTimeout(timeout);
            T local = default(T);
            int num = BlockingCollection<T>.TryAddRemoveAny(collections, ref local, (int)timeout.TotalMilliseconds, false);
            item = local;
            return num;
        }


        private static int TryAddRemoveAny(BlockingCollection<T>[] collections, ref T item, int millisecondsTimeout, bool operationMode)
        {
            BlockingCollection<T>[] blockingsArray = BlockingCollection<T>.ValidateCollectionsArray(collections, operationMode);
            Stopwatch stopwatch = null;
            int num2 = millisecondsTimeout;
            long elapsedMilliseconds = 0L;
            WaitHandle[] waitHandles = new WaitHandle[blockingsArray.Length];
            
            if (operationMode)
            {
                for (int i = 0; i < blockingsArray.Length; i++)
                {
                    if (blockingsArray[i].mFreeNodes != null)
                    {
                        waitHandles[i] = blockingsArray[i].mFreeNodes.AvailableWaitHandle;
                    }
                    else
                    {
                        blockingsArray[i].TryAdd(item);
                        return i;
                    }
                }
            }
            else if (!operationMode)
            {
                for (int j = 0; j < blockingsArray.Length; j++)
                    waitHandles[j] = blockingsArray[j].mOccupiedNodes.AvailableWaitHandle;
            }

            if (millisecondsTimeout > 0)
            {
                stopwatch = new Stopwatch();
                stopwatch.Start();
            }
            while ((millisecondsTimeout == -1) || (num2 >= 0))
            {
                int index = WaitHandle.WaitAny(waitHandles, num2, false);
                if (index == 0x102)
                {
                    return -1;
                }
                if (operationMode)
                {
                    try
                    {
                        if (blockingsArray[index].TryAdd(item)) return index;
                        return index;
                    }
                    catch (InvalidOperationException)
                    {
                        throw new ArgumentException("collections");
                    }
                }

                if ((!operationMode) && blockingsArray[index].TryRemove(out item))
                    return index;
                
                if (millisecondsTimeout > 0)
                {
                    elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
                    if (elapsedMilliseconds > 0x7fffffffL)
                        return -1;

                    num2 -= (int)elapsedMilliseconds;
                }
                else if (millisecondsTimeout == 0)
                    return -1;
            }
            return -1;
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        #region Private Helper functions
        
        /// <summary>Cancels the semaphores.</summary>
        private void CancelSemaphores()
        {
            this.mOccupiedNodes.Cancel();
            if (this.mFreeNodes != null)
                this.mFreeNodes.Cancel();
        }

        /// <summary>Throws a System.OperationCanceledException exception if the collection was in an interrupted state.</summary>
        /// <exception cref="T:System.InvalidOperationException">If the collection has been marked as complete for adds.</exception>
        private void CheckAddingIsCompleted()
        {
            if (this.mIsAddingCompleted == 1)
                throw new InvalidOperationException("BlockingCollection is completed");
        }

        /// <summary>Throws a System.ObjectDisposedException if the collection was disposed</summary>
        /// <exception cref="T:System.ObjectDisposedException">If the collection has been disposed.</exception>
        private void CheckDisposed()
        {
            if (this.mIsDisposed)
                throw new ObjectDisposedException("BlockingCollection", "BlockingCollection Disposed");
            
        }

        /// <summary>
        /// This method will be used whenever it's desired to release a semaphore. This is because if the 
        /// semaphore being released was canceled, this semaphore will throw an OperationCanceledException which 
        /// is not of real use for purposes of BlockingCollection.</summary>
        /// <param name="semaphore">The semaphore being released</param>
        private static void ReleaseSemaphore(SemaphoreSlim semaphore)
        {
            try
            {
                semaphore.Release();
            }
            catch (OperationCanceledException)
            {
            }
        }

        /// <summary>Centralizes the logic of validating the millisecondsTimeout input argument.</summary>
        /// <param name="millisecondsTimeout">The number of milliseconds to wait for to successfully complete an 
        /// operation on the collection.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">If the number of millseconds is less than 0 and not 
        /// equal to Timeout.Infinite.</exception>
        private static void ValidateMillisecondsTimeout(int millisecondsTimeout)
        {
            if ((millisecondsTimeout < 0) && (millisecondsTimeout != -1))
            {
                throw new ArgumentOutOfRangeException("millisecondsTimeout");
            }
        }

        /// <summary>Centeralizes the logic of validating the timeout input argument.</summary>
        /// <param name="timeout">The TimeSpan to wait for to successfully complete an operation on the collection.</param>
        /// <exception cref="T:System.ArgumentOutOfRangeException">If the number of millseconds represented by the timeout 
        /// TimeSpan is less than 0 or is larger than Int32.MaxValue and not Timeout.Infinite</exception>
        private static void ValidateTimeout(TimeSpan timeout)
        {
            long totalMilliseconds = (long)timeout.TotalMilliseconds;
            if (((totalMilliseconds < 0L) || (totalMilliseconds > 0x7fffffffL)) && (totalMilliseconds != -1L))
            {
                throw new ArgumentOutOfRangeException("timeout");
            }
        }


        private static BlockingCollection<T>[] ValidateCollectionsArray(BlockingCollection<T>[] collections, bool operationMode)
        {
            if (collections == null || collections.Length < 1)
                throw new ArgumentNullException("collections");


            BlockingCollection<T>[] blockingsArray = new BlockingCollection<T>[collections.Length];
            for (int i = 0; i < blockingsArray.Length; i++)
            {
                blockingsArray[i] = collections[i];
                if (blockingsArray[i] == null)
                    throw new ArgumentException("collections");

                try
                {
                    blockingsArray[i].CheckDisposed();
                }
                catch (ObjectDisposedException)
                {
                    throw new ObjectDisposedException("collections");
                }

                if (operationMode)
                {
                    try
                    {
                        blockingsArray[i].CheckAddingIsCompleted();
                    }
                    catch (InvalidOperationException)
                    {
                        throw new ArgumentException("collections");
                    }
                }
            }

            return blockingsArray;
        }

        #endregion

        /*////////////////////////////////////////////////////////////////////////////////////////////////*/

        /// <summary>
        /// Marks the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instances
        /// as not accepting any more additions.  
        /// </summary>
        /// <remarks>
        /// After a collection has been marked as complete for adding, adding to the collection is not permitted 
        /// and attempts to remove from the collection will not wait when the collection is empty.
        /// </remarks>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <exception cref="T:System.InvalidOperationException">The collection has already been marked as complete for adding.</exception>
        public void CompleteAdding()
        {
            CheckDisposed();
            Interlocked.CompareExchange(ref mIsAddingCompleted, 1, 0);
            
            if (Count == 0) CancelSemaphores();
        }

        /// <summary>
        /// Releases resources used by the <see cref="T:ParallelRuntimeLibrary.Collections.BlockingCollection{T}" /> instance.</summary>
        public void Dispose()
        {
            if (!this.mIsDisposed)
            {
                IDisposable collection = this.mCollection as IDisposable;
                if (collection != null)
                    collection.Dispose();
                
                if (this.mFreeNodes != null)
                    this.mFreeNodes.Dispose();
                
                this.mOccupiedNodes.Dispose();
                this.mIsDisposed = true;
            }
        }

        /// <summary>
        /// Copies the items from the <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> instance into a new array.</summary>
        /// <returns>An array containing copies of the elements of the collection.</returns>
        /// <exception cref="T:System.ObjectDisposedException">The <see cref="T:System.Threading.Collections.BlockingCollection{T}" /> has been disposed.</exception>
        /// <remarks>
        /// The copied elements are not removed from the collection.
        /// </remarks>
        public T[] ToArray()
        {
            this.CheckDisposed();
            return this.mCollection.ToArray();
        }

    }
}
